package pl.com.sthaether.mc.event;


import pl.com.sthaether.mc.config.MCMainConfiguration;
import pl.com.sthaether.mc.exception.MCExceptionHandler;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Logger;

public class MCEventBus {

    private static Logger logger = Logger.getLogger("Event bus logger");

    private final ExecutorService executorService = Executors.newFixedThreadPool(MCMainConfiguration.THREAD_COUNT);
    private Map<MCEventType, Set<MCEventListener>> listeners = new ConcurrentHashMap<>();
    private Set<MCEventType> disabledListenersTypes = new HashSet<>();

    private MCEventBus() {
    }

    public static MCEventBus getBus() {
        return MCEventBusHolder.getBus();
    }

    public void resolveParallelEvent(MCParallelEvent eventToResolve) {
        try {
            if (!disabledListenersTypes.contains(eventToResolve.getType())) {
                logger.info("Registering event " + eventToResolve.getName());
                executorService.submit(eventToResolve);
            } else {
                logger.info("Event " + eventToResolve.getName() + " not resolved - " + eventToResolve.getType().name() + " disabled");
            }
        } catch (Exception e) {
            MCExceptionHandler.handleException(e);
        }
    }

    public <T> Object doWork(MCWork<T> workToDo) { //TODO #KK #TO_TEST - not sure if this will work in case of returning types
        try {
            if (!disabledListenersTypes.contains(workToDo.getType())) {
                logger.info("Registering work " + workToDo.getName());
                return executorService.submit(workToDo).get();
            } else {
                logger.info("Event " + workToDo.getName() + " not resolved - " + workToDo.getType().name() + " disabled");
                return workToDo.getFailedItem();
            }
        } catch (Exception e) {
            MCExceptionHandler.handleException(e);
        } finally {
            return workToDo.getFailedItem();
        }
    }

    protected void registerEventDone(MCEvent<?> event) {
        if (!disabledListenersTypes.contains(event.getType())) {
            logger.info("Event " + event.getName() + " completed");
            for (MCEventListener listener : listeners.get(event.getType())) {
                listener.actionPerformed(event);
            }
        }
    }

    public void addListener(MCEventListener listener) {
        for (MCEventType type : listener.getInterestedTypes()) {
            if (listeners.get(type) == null) {
                listeners.put(type, new HashSet<>());
            }
            listeners.get(type).add(listener);
        }
    }

    public void disableListenerType(MCEventType type) {
        logger.info(type.name() + " disabled");
        disabledListenersTypes.add(type);
    }

    public void enableListenerType(MCEventType type) {
        logger.info(type.name() + " enabled");
        disabledListenersTypes.remove(type);
    }

    private static class MCEventBusHolder {
        private static final MCEventBus INSTANCE = new MCEventBus();

        private static MCEventBus getBus() {
            return INSTANCE;
        }
    }


}
